/*
* ----------------------------------------------------------------------------
* THE COFFEEWARE LICENSE (Revision 1):
* <ihsan@kehribar.me> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a coffee in return.
* -----------------------------------------------------------------------------
* This library is based on this library: 
*   https://github.com/aaronds/arduino-nrf24l01
* Which is based on this library: 
*   http://www.tinkerer.eu/AVRLib/nRF24L01
* -----------------------------------------------------------------------------
*/
#include "nrf24.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
#include <stdio.h>
#include <stdbool.h>

/* Externs from the nwk stack, called in radio layer */
extern void nwk_tx_result_cb(bool completed);
extern void nwk_receive_pkt_cb(void);

/* init the hardware pins */
void nrf24_init() 
{
//    nrf24_setupPins(); //done in initPorts();
    nrf24_ce_make(LOW);
    nrf24_csn_make(HIGH);    
}

/* configure the module */
void nrf24_config(uint8_t channel)
{
	//TODO1: enable all 3 interrupt types
    // Set RF channel
    // Carrier Frequency = 2400 + channel [MHz] //up to 2525 MHz
    nrf24_configRegister(RF_CH,channel);

    // Set length of incoming payload 
    /* Static payload lengths aren't used anymore */
//    nrf24_configRegister(RX_PW_P0, 0x00); // Auto-ACK pipe ...
//    nrf24_configRegister(RX_PW_P1, 0x00); // Data payload pipe
//    nrf24_configRegister(RX_PW_P2, 0x00); // Pipe not used
//    nrf24_configRegister(RX_PW_P3, 0x00); // Pipe not used
//    nrf24_configRegister(RX_PW_P4, 0x00); // Pipe not used
//    nrf24_configRegister(RX_PW_P5, 0x00); // Pipe not used

    // 1 Mbps, TX gain: 0dbm
    nrf24_configRegister(RF_SETUP, (0<<RF_DR)|((0x03)<<RF_PWR));

    // CRC enable, 1 byte CRC length
    nrf24_configRegister(CONFIG,NRF24_CONFIG_ALL_INTS);

    // Auto Acknowledgment
    nrf24_configRegister(EN_AA,(1<<ENAA_P0)|(1<<ENAA_P1)|(0<<ENAA_P2)|(1<<ENAA_P3)|(0<<ENAA_P4)|(0<<ENAA_P5));

    // Enable RX addresses
    nrf24_configRegister(EN_RXADDR,(1<<ERX_P0)|(1<<ERX_P1)|(0<<ERX_P2)|(1<<ERX_P3)|(0<<ERX_P4)|(0<<ERX_P5));

    // Auto retransmit delay: 1000 us and Up to 15 retransmit trials
//    nrf24_configRegister(SETUP_RETR,(0x04<<ARD)|(0x0F<<ARC));
    nrf24_configRegister(SETUP_RETR,(0x04<<ARD)|(0x03<<ARC));

    // Enable dynamic payload feature
    nrf24_configRegister(FEATURE, (1<<EN_DPL) | (1<<EN_DYN_ACK));

    // Dynamic length configurations: No dynamic length
//    nrf24_configRegister(DYNPD,(0<<DPL_P0)|(0<<DPL_P1)|(0<<DPL_P2)|(0<<DPL_P3)|(0<<DPL_P4)|(0<<DPL_P5));
    nrf24_configRegister(DYNPD,(1<<DPL_P0)|(1<<DPL_P1)|(0<<DPL_P2)|(1<<DPL_P3)|(0<<DPL_P4)|(0<<DPL_P5));

    // Start listening
    nrf24_powerUpRx();
}

/* Set the RX address */
void nrf24_rx_address(uint8_t * adr, uint8_t pipe)
{
    nrf24_ce_make(LOW);
    switch(pipe)
    {
		case 0:
			/* This should only be set when you set the tx address since they have to be same for auto-ack */
			//nrf24_writeRegister(RX_ADDR_P0,adr,NRF24_ADDR_LEN);
			break;
		case 1:
			nrf24_writeRegister(RX_ADDR_P1,adr,NRF24_ADDR_LEN);
			break;
		case 2:
			nrf24_writeRegister(RX_ADDR_P2,adr,NRF24_ADDR_LEN);
			break;
		case 3:
			nrf24_writeRegister(RX_ADDR_P3,adr,NRF24_ADDR_LEN);
			break;
		case 4:
			nrf24_writeRegister(RX_ADDR_P4,adr,NRF24_ADDR_LEN);
			break;
		case 5:
			nrf24_writeRegister(RX_ADDR_P5,adr,NRF24_ADDR_LEN);
			break;
		default: /* pipe does not exist, do nothing */
			break;
    }
    nrf24_ce_make(HIGH);
}

/* Set the TX address */
void nrf24_tx_address(uint8_t* adr)
{
    /* RX_ADDR_P0 must be set to the sending addr for auto ack to work. */
    nrf24_writeRegister(RX_ADDR_P0,adr,NRF24_ADDR_LEN);
    nrf24_writeRegister(TX_ADDR,adr,NRF24_ADDR_LEN);
}

/* Checks if data is available for reading */
/* Returns 1 if data is ready ... */
uint8_t nrf24_dataReady() 
{
    // See note in getData() function - just checking RX_DR isn't good enough
    uint8_t status = nrf24_getStatus();

    // We can short circuit on RX_DR, but if it's not set, we still need
    // to check the FIFO for any pending packets
    if ( status & (1 << RX_DR) ) 
    {
        return 1;
    }

    return (uint8_t)(nrf24_rxFifoEmpty() == 0);
}

/* Checks if receive FIFO is empty or not */
uint8_t nrf24_rxFifoEmpty(void)
{
    uint8_t fifoStatus = 0;

    nrf24_readRegister(FIFO_STATUS,&fifoStatus,1);
    
    return (fifoStatus & (1 << RX_EMPTY));
}

bool nrf24_rf_signal_present(void)
{
	uint8_t power_detect_reg = 0;
	nrf24_readRegister(CD, &power_detect_reg, 1);
	return (power_detect_reg & _BV(RPD));
}

/* Returns the length of data waiting in the RX fifo */
uint8_t nrf24_payloadLength(void)
{
    uint8_t status;
    nrf24_csn_make(LOW);
    spi_transfer(R_RX_PL_WID);
    status = spi_transfer(0x00);
    nrf24_csn_make(HIGH);
    if(status <= 32)
    {
    	return status;
    }else /* If we got bad length, flush RX and return an error code */
    {
        nrf24_csn_make(LOW); /* Pull down chip select */
        spi_transfer(FLUSH_RX); /* Write cmd to flush RX FIFO */
        nrf24_csn_make(HIGH); /* Pull up chip select */

    	return BAD_PAYLOAD_LENGTH;
    }
}

/* Returns which data pipe data waiting in the RX fifo is on */
uint8_t nrf24_getRxPipe(void)
{
	uint8_t status = nrf24_getStatus();
	status &= RX_P_NO_MASK; /* mask off the rx pipe number bits */
	return (status >> RX_P_NO); /* shift it down 1 since it starts at bit 1 but we just want the number */
}

/* Reads payload bytes into data array */
void nrf24_getData(uint8_t* data, uint8_t length)
{
    if(length <= 32){
    	/* Pull down chip select */
		nrf24_csn_make(LOW);

		/* Send cmd to read rx payload */
		spi_transfer( R_RX_PAYLOAD );

		/* Read payload */
		nrf24_transferSync(data, data, length);

		/* Pull up chip select */
		nrf24_csn_make(HIGH);

		/* Reset status register */
		nrf24_configRegister(STATUS,(1<<RX_DR));
    }

}

/* Returns the number of retransmissions occurred for the last message */
uint8_t nrf24_retransmissionCount()
{
    uint8_t rv;
    nrf24_readRegister(OBSERVE_TX,&rv,1);
    rv = rv & 0x0F;
    return rv;
}

// Sends a data package to the default address. Be sure to send the correct
// amount of bytes as configured as payload on the receiver.
void nrf24_send(uint8_t* value, uint8_t length)
{    
    /* Go to Standby-I first */
    nrf24_ce_make(LOW);
     
    /* Set to transmitter mode , Power up if needed */
    nrf24_powerUpTx();

    /* Do we really need to flush TX fifo each time ? */
    #if 1
        /* Pull down chip select */
        nrf24_csn_make(LOW);           

        /* Write cmd to flush transmit FIFO */
        spi_transfer(FLUSH_TX);     

        /* Pull up chip select */
        nrf24_csn_make(HIGH);                    
    #endif 

    /* Pull down chip select */
    nrf24_csn_make(LOW);

    /* Write cmd to write payload */
    spi_transfer(W_TX_PAYLOAD);

    /* Write payload */
    nrf24_transmitSync(value, length);

    /* Pull up chip select */
    nrf24_csn_make(HIGH);

    /* Start the transmission */
    nrf24_ce_make(HIGH);    
}

uint8_t nrf24_isSending()
{
    uint8_t status;

    /* read the current status */
    status = nrf24_getStatus();
                
    /* if sending successful (TX_DS) or max retries exceded (MAX_RT). */
    if((status & ((1 << TX_DS)  | (1 << MAX_RT))))
    {        
        return 0; /* false */
    }

    return 1; /* true */

}

uint8_t nrf24_getStatus()
{
    uint8_t rv;
    nrf24_csn_make(LOW);
    rv = spi_transfer(NOP);
    nrf24_csn_make(HIGH);
    return rv;
}

uint8_t nrf24_lastMessageStatus()
{
    uint8_t rv;

    rv = nrf24_getStatus();

    /* Transmission went OK */
    if((rv & ((1 << TX_DS))))
    {
        return NRF24_TRANSMISSON_OK;
    }
    /* Maximum retransmission count is reached */
    /* Last message probably went missing ... */
    else if((rv & ((1 << MAX_RT))))
    {
        return NRF24_MESSAGE_LOST;
    }  
    /* Probably still sending ... */
    else
    {
        return 0xFF;
    }
}

void nrf24_powerUpRx()
{     
    nrf24_csn_make(LOW);
    spi_transfer(FLUSH_RX);
    nrf24_csn_make(HIGH);

    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT)); 

    nrf24_ce_make(LOW);    
    nrf24_configRegister(CONFIG, NRF24_CONFIG_ALL_INTS|((1<<PWR_UP)|(1<<PRIM_RX)));
    nrf24_ce_make(HIGH);
}

void nrf24_powerUpTx()
{
    nrf24_configRegister(STATUS,(1<<RX_DR)|(1<<TX_DS)|(1<<MAX_RT)); 

    nrf24_configRegister(CONFIG, NRF24_CONFIG_ALL_INTS|((1<<PWR_UP)|(0<<PRIM_RX)));
}

void nrf24_powerDown()
{
    nrf24_ce_make(LOW);
    nrf24_configRegister(CONFIG,NRF24_CONFIG_ALL_INTS);
}

/* hardware spi routine */
uint8_t spi_transfer(uint8_t tx)
{
	SPDR = tx;	//shift out byte to send
	while(!(SPSR & _BV(SPIF)));//wait for transfer to finish
	return SPDR; //return byte shifted in
}

/* send and receive multiple bytes over SPI */
void nrf24_transferSync(uint8_t* dataout, uint8_t* datain, uint8_t len)
{
    uint8_t i;
    for(i = 0; i < len; ++i)
    {
        datain[i] = spi_transfer(dataout[i]);
    }

}

/* send multiple bytes over SPI */
void nrf24_transmitSync(uint8_t* dataout, uint8_t len)
{
    uint8_t i;
    for(i = 0; i < len; ++i)
    {
        spi_transfer(dataout[i]);
    }

}

/* Clocks only one byte into the given nrf24 register */
void nrf24_configRegister(uint8_t reg, uint8_t value)
{
    nrf24_csn_make(LOW);
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    spi_transfer(value);
    nrf24_csn_make(HIGH);
}

/* Read single register from nrf24 */
void nrf24_readRegister(uint8_t reg, uint8_t* value, uint8_t len)
{
    nrf24_csn_make(LOW);
    spi_transfer(R_REGISTER | (REGISTER_MASK & reg));
    nrf24_transferSync(value,value,len);
    nrf24_csn_make(HIGH);
}

/* Write to a single register of nrf24 */
void nrf24_writeRegister(uint8_t reg, uint8_t* value, uint8_t len) 
{
    nrf24_csn_make(LOW);
    spi_transfer(W_REGISTER | (REGISTER_MASK & reg));
    nrf24_transmitSync(value,len);
    nrf24_csn_make(HIGH);
}

/* Performs an atomic Read-Modify-Write bitset on a single nrf24 register
 * Example: set bits 0 and 6 of STATUS register
 * 		nrf24_setBits_reg(STATUS, (_BV(0) | _BV(6)));  */
void nrf24_setBits_reg(uint8_t reg, uint8_t bits)
{
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
	{
		uint8_t reg_val;
		nrf24_readRegister(reg, &reg_val, 1); /* read the register */
		reg_val |= bits; /* modify it's clean value */
		nrf24_writeRegister(reg, &reg_val, 1); /* write the modified value to the register */
	}
}

/* Performs an atomic Read-Modify-Write bitclr on a single nrf24 register
 * Example: clr bits 1 and 7 of STATUS register
 * 		nrf24_setBits_reg(STATUS, (_BV(1) | _BV(7)));  */
void nrf24_clrBits_reg(uint8_t reg, uint8_t bits)
{
	ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
	{
		uint8_t reg_val;
		nrf24_readRegister(reg, &reg_val, 1); /* read the register */
		reg_val &= ~bits; /* modify it's clean value */
		nrf24_writeRegister(reg, &reg_val, 1); /* write the modified value to the register */
	}
}

//GORDON: TODO2: find a better place to put this code... it's radio specific but not sure it belong in radio section
ISR(INT0_vect)
{
	uint8_t status = nrf24_getStatus();
	if(( status & _BV(TX_DS) ) || ( status & _BV(MAX_RT) ))
		nwk_tx_result_cb(status & _BV(TX_DS));//param: bool completed
	else if(( status & _BV(RX_DR) ))
		nwk_receive_pkt_cb(); //TODO2: check the value of this return
	//else - WTF? how'd this interrupt happen...
}
